﻿using System;
using System.Collections.Generic;
using System.Device.Location;
using System.Diagnostics;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
using System.Xml.Linq;
using System.Xml.Serialization;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Controls.Maps;
using Microsoft.Phone.Net.NetworkInformation;

namespace DenpaMap
{
    public partial class MainPage : PhoneApplicationPage
    {
        //XDocument doc;
        IList<SpeedTestContainer> speedList;// = new List<SpeedTestContainer>();
        private GeoCoordinateWatcher watcher;
        bool isGeoOn = false;
        byte polocyVer;
        double latitude, longitude;
        IsolatedStorageSettings store = IsolatedStorageSettings.ApplicationSettings;
        // コンストラクター
        public MainPage()
        {
            InitializeComponent();

            //位置情報サービスを初期化する
            watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);

            //PositionChangedイベントを受け取る感覚をメートル単位で指定
            watcher.MovementThreshold = 20;
            watcher.StatusChanged += watcher_StatusChanged;
            watcher.PositionChanged += watcher_PositionChanged;

            //位置情報の使用許諾
            if (!store.Contains("policy"))
                SetPermission();
            else if(!(bool)store["policy"])
                SetPermission();


            if (store.Contains("data"))
            {
                string data = (string)store["data"];
                //Stream data = new MemoryStream(System.Text.Encoding.Unicode.GetBytes((string)store["data"]));
                //data.Seek(0, SeekOrigin.Begin);
                speedList = Deserialize(data);
                if (speedList == null)
                {
                    speedList = new List<SpeedTestContainer>();
                    return;
                }
                foreach (var item in speedList)
                {
                    //  ☝( ◠‿◠ )☝ 現在地に紫色のピンを立てる
                    var pin = new Pushpin()
                    {
                        Location = new GeoCoordinate(item.Latitude, item.Longitude),
                        Content = "" + item.Speed.ToString("Speed: #.###") + "Mbps\nType: " + item.Type,
                        Background = new SolidColorBrush(item.TypeColor),
                    };
                    map1.Children.Add(pin);
                }
            }
            if (speedList == null)
            {
                speedList = new List<SpeedTestContainer>();
                return;
            }
        }

        private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
        {
            if (isGeoOn)
                watcher.Start();
            else
                watcher.Stop();
        }

        void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
        {

            Deployment.Current.Dispatcher.BeginInvoke(() =>
            {

                latitude = e.Position.Location.Latitude;
                longitude = e.Position.Location.Longitude;

                // データを取得した位置を中心にする
                map1.Center = new GeoCoordinate(latitude, longitude);

            });
        }

        private bool SetPermission()
        {
            if (MessageBox.Show("このアプリケーションでは端末の位置情報と回線速度を関連づけて端末内に保存します。\n保存された情報はアプリケーションが自動的に外部に送信することはありません。\n使用許可は「設定」ページからいつでも取り消すことができます。\n\n位置情報を使用したくない場合は「キャンセル」をタップしてアプリケーションを終了してください。\n位置情報の使用を許可しますか？", "位置情報の使用許可", MessageBoxButton.OKCancel)
                    == MessageBoxResult.OK)
            {
                isGeoOn = true;
                polocyVer = 1;
                store["policy"] = true;
                store["policyVer"] = polocyVer;
                store.Save();
                return true;
            }
            else
            {
                var xna = new Microsoft.Xna.Framework.Game();
                xna.Exit();
                while (true) ; //Exitが非同期だから死ぬまで待つ
            }
        }

        private void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
        {
            switch (e.Status)
            {
                //位置情報サービスが使用できない状態
                case GeoPositionStatus.Disabled:
                    if (watcher.Permission == GeoPositionPermission.Denied)
                    {
                        MessageBox.Show("位置情報サービスが無効になっています。");
                    }
                    else
                    {
                        MessageBox.Show("位置情報サービスを使用できません。");
                    }
                    //if (watcher.Position.Location.IsUnknown)
                    //    searchBtn.IsEnabled = false;
                    break;
                //位置情報サービスが初期化中の状態
                case GeoPositionStatus.Initializing:
                    //MessageBox.Show("位置情報サービスを準備しています。");
                    break;
                //位置情報サービスが位置情報を取得できない状態
                case GeoPositionStatus.NoData:
                    MessageBox.Show("データが取得できません。");
                    //if (watcher.Position.Location.IsUnknown)
                    //    searchBtn.IsEnabled = false;
                    break;
                //位置情報を取得できる状態
                case GeoPositionStatus.Ready:
                    //message.Text = "";
                    //searchBtn.IsEnabled = true;
                    break;
            }
        }

        private void benchButton_Click(object sender, EventArgs e)
        {
            GetDownloadSpeed(delegate(SpeedTestContainer sc)
            {
                Dispatcher.BeginInvoke(() =>
                {
                    //  ☝( ◠‿◠ )☝ 現在地に紫色のピンを立てる
                    var pin = new Pushpin()
                    {
                        Location = new GeoCoordinate(sc.Latitude, sc.Longitude),
                        Content = "" + sc.Speed.ToString("Speed: #.###") + "Mbps\nType: "+sc.Type,
                        Background = new SolidColorBrush(sc.TypeColor),
                    };
                    map1.Children.Add(pin);
                    speedList.Add(sc);
                });
            });
        }

        /// <summary>
        /// ダウンロード速度を計測して返します。
        /// </summary>
        private void GetDownloadSpeed(Action<SpeedTestContainer> act)
        {
            bool flag1 = true;
            Random rnd = new Random();

            var wc = new WebClient { 
                AllowReadStreamBuffering = false    
            };
            System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

            wc.OpenReadCompleted += new OpenReadCompletedEventHandler(delegate(object sender, OpenReadCompletedEventArgs e){
                if (flag1){
                    flag1 = false;
                    sw.Start();
                    //地球上で最も美しいデータ
                    wc.OpenReadAsync(new Uri("http://denpamap.googlecode.com/git/testdata.bin?rnd=" + rnd.Next()));
                }
                else{
                    sw.Stop();
                    SpeedTestContainer sc = new SpeedTestContainer();
                    sc.Latitude = latitude;
                    sc.Longitude = longitude;
                    var nt = NetworkInterface.NetworkInterfaceType;
                    switch (nt)
                    {
                        case NetworkInterfaceType.Ethernet:
                            sc.Type = "Ethernet";
                            sc.TypeColor = Colors.Green;
                            break;
                        case NetworkInterfaceType.Wireless80211:
                            // 無線LANに接続された状態
                            sc.Type = "WLAN";
                            sc.TypeColor = Colors.Blue;
                            break;
                        case NetworkInterfaceType.MobileBroadbandGsm:
                        case NetworkInterfaceType.MobileBroadbandCdma:
                            sc.Type = "3G";
                            sc.TypeColor = Colors.Red;
                            break;
                            
                    }
                    sc.Speed = 10240.0 / sw.ElapsedMilliseconds;
                    Debug.WriteLine("" + sw.ElapsedMilliseconds);
                    act(sc);
                }
            });
            wc.OpenReadAsync(new Uri("http://denpamap.googlecode.com/git/testdata.zip?rnd="+rnd.Next()));
              
            return;
        }

        public static void Serialize(Stream streamObject, object objForSerialization)
        {
            if (objForSerialization == null || streamObject == null)
                return;

            XmlSerializer serializer = new XmlSerializer(objForSerialization.GetType());
            serializer.Serialize(streamObject, objForSerialization);
        }

        public static IList<SpeedTestContainer> Deserialize(string streamObject)
        {
            try
            {
                /*if (serializedObjectType == null || streamObject == null)
                    return null;

                streamObject.Seek(0, SeekOrigin.Begin);
                XmlSerializer serializer = new XmlSerializer(serializedObjectType);
                return serializer.Deserialize(streamObject);*/
                var doc = XDocument.Parse(streamObject);
                var data = from item in doc.Descendants("SpeedTestContainer")
                           select new SpeedTestContainer
                           {
                               Latitude = double.Parse(item.Element("Latitude").Value),
                               Longitude = double.Parse(item.Element("Longitude").Value),
                               Speed = double.Parse(item.Element("Speed").Value),
                               Type = item.Element("Type").Value,
                               TypeColor = new Color()
                               {
                                   A = byte.Parse(item.Element("TypeColor").Element("A").Value),
                                   R = byte.Parse(item.Element("TypeColor").Element("R").Value),
                                   G = byte.Parse(item.Element("TypeColor").Element("G").Value),
                                   B = byte.Parse(item.Element("TypeColor").Element("B").Value)
                               }
                           };
                IList<SpeedTestContainer> r = data.ToList<SpeedTestContainer>();
                return r;

            }
            catch
            {
                return null;
            }
        }

        protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
        {
            IsolatedStorageSettings store = IsolatedStorageSettings.ApplicationSettings;
            Stream data = new MemoryStream();
            Serialize(data, speedList);
            //System.Xml.Serialization.XmlSerializer serializer =
            //    new System.Xml.Serialization.XmlSerializer(typeof(SpeedTestContainer));
            data.Seek(0, SeekOrigin.Begin);
            store["data"] = new StreamReader(data).ReadToEnd();
            string test = (string)store["data"];
            store.Save();
            base.OnNavigatedFrom(e);
        }

        private void settingButton_Click(object sender, EventArgs e)
        {
            NavigationService.Navigate(new Uri("/Setting.xaml", UriKind.RelativeOrAbsolute));
        }
    }

    public class SpeedTestContainer
    {
        [XmlElement]
        public string Type { get; set; }
        [XmlElement]
        public Color TypeColor { get; set; }
        [XmlElement]
        public double Longitude { get; set; }
        [XmlElement]
        public double Latitude { get; set; }
        [XmlElement]
        public double Speed { get; set; }
    }

}